Skip to content

release: MVP feature enhancements and documentation config update#27

Merged
salmanabdurrahman merged 5 commits intomainfrom
dev
Oct 15, 2025
Merged

release: MVP feature enhancements and documentation config update#27
salmanabdurrahman merged 5 commits intomainfrom
dev

Conversation

@salmanabdurrahman
Copy link
Copy Markdown
Member

Summary

Merging the dev branch into main for the next MVP release, which introduces multiple improvements and new functionalities.

Highlights

  • Protected daily content route using auth middleware.
  • Updated user settings validation for better UX.
  • Added like/unlike functionality for community posts.
  • Introduced DOCS_URL environment variable for documentation reference.
  • Updated configuration and README accordingly.

Deployment Notes

  • Run database migrations before deploying:

    npx prisma migrate deploy
  • Ensure the production environment has DOCS_URL properly set.

- add DOCS_URL to .env.example
- read DOCS_URL from environment variables using config
- pass documentationUrl to welcome view from config
- add docs url to config file
- update readme with docs url explanation
- add community post like table to prisma schema
- create community post like table migration
- implement toggleLikeOnPost service function to handle like/unlike logic
- update addLikeHandler to use toggleLikeOnPost service
- return like status and count in the response
- add user id validation to addLikeHandler
…idation, and clean up error paths

- Protect daily content route with requireAuth middleware
- Allow shorter 'userWhy' in updateUserSettingsSchema (min length 3)
- Remove prefix from zod error path for cleaner error messages
…y features (#26)

feat: MVP patches and improvements for auth, validation, and community features
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Prepares the next MVP release by securing routes, improving validation UX, introducing like/unlike functionality for community posts, and externalizing the docs URL into configuration.

  • Protects daily content route with authentication middleware
  • Adds toggle like/unlike with persistent like records and like count updates
  • Introduces DOCS_URL config and updates welcome page to use it

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/middleware/validate.middleware.ts Normalizes Zod error paths by stripping body/query/params prefixes for cleaner client errors
src/config/index.ts Adds docsUrl sourced from DOCS_URL env var
src/api/welcome/welcome.controller.ts Uses config.docsUrl for documentation link instead of hardcoded value
src/api/users/user.validation.ts Lowers minimum length for userWhy to 3 with updated message
src/api/content/content.routes.ts Secures /daily route with requireAuth middleware
src/api/community/community.service.ts Replaces addLike with toggleLike using CommunityPostLike join table and likeCount inc/dec in transactions
src/api/community/community.controller.ts Updates like endpoint to toggle, includes user auth check, and returns isLiked + count
prisma/schema.prisma Adds CommunityPostLike model and relations
prisma/migrations/.../migration.sql Creates CommunityPostLike table, indexes, and FKs
README.md Documents DOCS_URL env var
.env.example Adds DOCS_URL placeholder

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +122 to +170
if (existingLike) {
const updatedPost = await prisma.$transaction([
prisma.communityPostLike.delete({
where: {
userId_postId: {
userId,
postId,
},
},
}),
prisma.communityPost.update({
where: {
id: postId,
},
data: {
likeCount: {
decrement: 1,
},
},
}),
]);
return {
likedCount: updatedPost[1].likeCount,
isLiked: false,
};
} else {
const updatedPost = await prisma.$transaction([
prisma.communityPostLike.create({
data: {
userId,
postId,
},
}),
prisma.communityPost.update({
where: {
id: postId,
},
data: {
likeCount: {
increment: 1,
},
},
}),
]);
return {
likedCount: updatedPost[1].likeCount,
isLiked: true,
};
}
Copy link

Copilot AI Oct 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a race between the existence check and the transaction, which can cause unique-constraint (P2002) or not-found (P2025) errors under concurrent like/unlike requests and return 500 instead of idempotent responses. Wrap each branch in try/catch and handle Prisma errors (treat P2002 as already liked and P2025 as already unliked), or move to an upsert-style flow (e.g., try create and on P2002 return isLiked=true without changing count, and similarly for delete on P2025) so the endpoint is idempotent under concurrency.

Copilot uses AI. Check for mistakes.
const post = await toggleLikeOnPost(userId, postId);
const message = post.isLiked ? 'Postingan berhasil disukai' : 'Suka pada postingan dibatalkan';

return successResponse(res, 200, message, post);
Copy link

Copilot AI Oct 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the response shape from { likeCount } to { likedCount, isLiked }, which is a breaking change for clients. To maintain backward compatibility, return likeCount with the original key (e.g., { likeCount: post.likedCount, isLiked }) and consider keeping likedCount as an alias only if necessary.

Suggested change
return successResponse(res, 200, message, post);
return successResponse(res, 200, message, {
likeCount: post.likedCount,
isLiked: post.isLiked,
// Optionally include likedCount as an alias for forward compatibility
// likedCount: post.likedCount,
// ...include other post properties if needed
});

Copilot uses AI. Check for mistakes.
const config = {
port: process.env.PORT || 3000,
databaseUrl: process.env.DATABASE_URL || '',
docsUrl: process.env.DOCS_URL || '',
Copy link

Copilot AI Oct 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Defaulting docsUrl to an empty string can render a blank documentation link on the welcome page when DOCS_URL is not set, regressing from the previous working hardcoded URL. Provide a sensible default (e.g., the prior Postman URL) or fallback in the controller: docsUrl: process.env.DOCS_URL || 'https://documenter.getpostman.com/view/38960737/2sB3QJQBZ8'.

Suggested change
docsUrl: process.env.DOCS_URL || '',
docsUrl: process.env.DOCS_URL || 'https://documenter.getpostman.com/view/38960737/2sB3QJQBZ8',

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +16
ALTER TABLE "public"."CommunityPostLike" ADD CONSTRAINT "CommunityPostLike_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "public"."CommunityPostLike" ADD CONSTRAINT "CommunityPostLike_postId_fkey" FOREIGN KEY ("postId") REFERENCES "public"."CommunityPost"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
Copy link

Copilot AI Oct 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] For a pure join table, ON DELETE CASCADE is typically preferred to avoid manual cleanup when deleting users or posts; RESTRICT will block deletions and can leave orphaned likeCount if deletes are forced elsewhere. Consider using ON DELETE CASCADE on both FKs.

Suggested change
ALTER TABLE "public"."CommunityPostLike" ADD CONSTRAINT "CommunityPostLike_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "public"."CommunityPostLike" ADD CONSTRAINT "CommunityPostLike_postId_fkey" FOREIGN KEY ("postId") REFERENCES "public"."CommunityPost"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "public"."CommunityPostLike" ADD CONSTRAINT "CommunityPostLike_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "public"."CommunityPostLike" ADD CONSTRAINT "CommunityPostLike_postId_fkey" FOREIGN KEY ("postId") REFERENCES "public"."CommunityPost"("id") ON DELETE CASCADE ON UPDATE CASCADE;

Copilot uses AI. Check for mistakes.
@salmanabdurrahman salmanabdurrahman merged commit 418df0c into main Oct 15, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants